Skip to content

feat: fork conversation from any point in AI message history#744

Open
chr1syy wants to merge 5 commits intoRunMaestro:rcfrom
chr1syy:feat/fork-conversation
Open

feat: fork conversation from any point in AI message history#744
chr1syy wants to merge 5 commits intoRunMaestro:rcfrom
chr1syy:feat/fork-conversation

Conversation

@chr1syy
Copy link
Copy Markdown
Contributor

@chr1syy chr1syy commented Apr 7, 2026

Summary

Closes #205

  • Adds a "Fork conversation" button (GitFork icon) on hover for user and AI messages in the AI terminal
  • Clicking forks the conversation up to that message into a new agent session, carrying full conversation context
  • The new session inherits all source config (custom path/args/env, SSH remote, model, context window, group)
  • Copies cwd and fullPath from source session so the forked agent spawns in the correct working directory

Implementation

  • New useForkConversation hook following the established Send-to-Agent spawn pattern
  • Props threaded through MainPanel → MainPanelContent → TerminalOutput → LogItem
  • Fork button visibility gated to AI mode, user/ai source messages only
  • System prompt and template variables applied to the spawned agent
  • Error handling with Sentry reporting and state cleanup on spawn failure

Test plan

  • Existing TerminalOutput tests pass (97/97)
  • TypeScript type check clean
  • Manual: hover over AI/user message, click fork icon, verify new session created with context
  • Manual: fork from agent with custom cwd, verify forked agent spawns in same directory
  • Manual: fork from SSH remote session, verify SSH config propagated

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features
    • Fork button shown on AI or user messages (AI mode) to branch a conversation up to that message.
    • Fork opens as a new active conversation and shows a success notification with an estimated token count.
    • The app attempts to start the agent for the fork; if startup fails an error entry is added and surfaced to the user.

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Apr 7, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 077e66bd-1a1a-405f-9cfc-396a07477733

📥 Commits

Reviewing files that changed from the base of the PR and between bcb7369 and dcbae46.

📒 Files selected for processing (1)
  • src/renderer/hooks/agent/useForkConversation.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/renderer/hooks/agent/useForkConversation.ts

📝 Walkthrough

Walkthrough

Adds a fork-conversation feature: exports useForkConversation, wires a handleForkConversation callback from App through MainPanel → MainPanelContent → TerminalOutput, and surfaces a fork button on AI/user logs to create and spawn a new forked session seeded with history up to the selected log.

Changes

Cohort / File(s) Summary
Fork Conversation Hook
src/renderer/hooks/agent/useForkConversation.ts, src/renderer/hooks/agent/index.ts
New exported hook useForkConversation: builds context from logs up to a chosen logId, creates a merged/forked session (copying many session configs), seeds it with system/context logs, appends and activates the session, estimates tokens, spawns the agent process, and handles spawn errors (Sentry capture and error-log rollback).
App & MainPanel Props Wiring
src/renderer/App.tsx, src/renderer/hooks/props/useMainPanelProps.ts
Instantiates useForkConversation as handleForkConversation in App and exposes it on memoized MainPanel props as onForkConversation; updated deps to include the handler.
MainPanel Prop Forwarding
src/renderer/components/MainPanel/types.ts, src/renderer/components/MainPanel/MainPanel.tsx, src/renderer/components/MainPanel/MainPanelContent.tsx
Adds optional onForkConversation?: (logId: string) => void to MainPanel types and forwards the prop through MainPanel → MainPanelContent.
Terminal Output UI & Memoization
src/renderer/components/TerminalOutput.tsx
Adds optional onForkConversation to TerminalOutputProps/LogItemProps; LogItemComponent conditionally renders a fork (GitFork) button for AI/user logs when in AI mode and callback is present, invoking onForkConversation(log.id); memo comparison updated to include the callback reference.

Sequence Diagram

sequenceDiagram
    participant User as "User"
    participant Terminal as "TerminalOutput"
    participant App as "App / MaestroConsoleInner"
    participant Hook as "useForkConversation"
    participant Sessions as "Session State"
    participant Agent as "Agent Host (process.spawn)"
    participant Toast as "Toast/Notifications"

    User->>Terminal: click fork button on log
    Terminal->>App: onForkConversation(logId)
    App->>Hook: invoke with logId
    Hook->>Sessions: read active session & slice logs up to logId
    Sessions-->>Hook: sliced logs
    Hook->>Sessions: createMergedSession (seeded logs, fork meta)
    Sessions-->>Hook: new session & tab
    Hook->>Agent: prepare spawn (flags, git/ssh, prompts)
    Hook->>Agent: window.maestro.process.spawn(...)
    alt spawn succeeds
        Agent-->>Hook: spawn ok
        Hook->>Sessions: mark tab busy, update session
        Hook->>Toast: show success + token estimate
    else spawn fails
        Agent-->>Hook: error
        Hook->>Sessions: append error log, set tab idle
    end
    Sessions-->>App: updated sessions state
    App->>Terminal: re-render showing new session/tab
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Poem

🐰 I nibble logs and split the trail,
A forked new tab with a curious tale,
Up to this line the story’s kept,
New branches bloom where answers slept,
Hop on — explore each winding veil.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 33.33% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main feature added: enabling users to fork conversations from any point in AI message history.
Linked Issues check ✅ Passed The implementation fulfills all coding requirements from issue #205: fork button on messages, new session creation with conversation history, configuration propagation, system prompt application, and proper error handling.
Out of Scope Changes check ✅ Passed All changes are directly related to the fork conversation feature. The new hook, prop threading through components, UI button addition, and configuration propagation are all within the stated objectives.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@greptile-apps
Copy link
Copy Markdown

greptile-apps bot commented Apr 7, 2026

Greptile Summary

This PR adds a "Fork conversation" button to user/AI messages in the AI terminal, spawning a new agent session pre-loaded with the conversation history up to that point. The implementation follows the established Send-to-Agent spawn pattern, correctly threads config from the source session (custom path/args/env, SSH, model, group), and includes proper Sentry error reporting with state cleanup on failure.

  • P1: The logIndex passed from onForkConversation(index) is the iteration index of filteredLogs — a search-filtered and collapsed view — not the position in sourceTab.logs. When output-search is active or consecutive AI responses are collapsed, sourceTab.logs.slice(0, logIndex + 1) cuts the conversation at the wrong message. The fix is to pass log.id at the call-site and resolve the actual index inside useForkConversation.

Confidence Score: 4/5

Safe to merge after fixing the logIndex/filteredLogs mismatch, which causes incorrect context slicing when search is active or AI responses are collapsed.

One P1 defect (wrong slice index in the fork hook) that causes incorrect conversation context in foreseeable real-world conditions; all other findings are P2.

src/renderer/hooks/agent/useForkConversation.ts (logIndex vs sourceTab.logs mismatch) and src/renderer/components/TerminalOutput.tsx (call-site needs to pass log.id instead of visual index)

Important Files Changed

Filename Overview
src/renderer/hooks/agent/useForkConversation.ts New fork hook — P1 logIndex/filteredLogs mismatch causes incorrect log slicing; stdout role labeling is a P2 semantic issue
src/renderer/components/TerminalOutput.tsx Fork button wired correctly to LogItemComponent; call-site passes visual map index instead of log.id, contributing to the P1 index bug
src/renderer/App.tsx Clean integration: useForkConversation hooked up with sessions, setSessions, activeSessionId, and setActiveSessionId
src/renderer/hooks/props/useMainPanelProps.ts handleForkConversation correctly threaded into MainPanel props and memoization dependency array
src/renderer/components/MainPanel/types.ts onForkConversation prop declaration added correctly to MainPanelProps interface
src/renderer/components/MainPanel/MainPanel.tsx onForkConversation passed through to MainPanelContent without issues
src/renderer/components/MainPanel/MainPanelContent.tsx onForkConversation forwarded to TerminalOutput cleanly
src/renderer/hooks/agent/index.ts useForkConversation correctly exported from the agent hooks barrel

Sequence Diagram

sequenceDiagram
    participant User
    participant TerminalOutput
    participant useForkConversation
    participant createMergedSession
    participant window.maestro.process

    User->>TerminalOutput: Hover + click GitFork button (on ai/user message)
    TerminalOutput->>useForkConversation: onForkConversation(filteredIndex)
    Note over useForkConversation: Looks up active session & tab
    useForkConversation->>useForkConversation: sourceTab.logs.slice(0, logIndex+1)
    Note over useForkConversation: ⚠️ filteredIndex ≠ sourceTab index when search active
    useForkConversation->>useForkConversation: Format logs as context message
    useForkConversation->>createMergedSession: Create new session (forkName, projectRoot, toolType)
    createMergedSession-->>useForkConversation: { session: newSession, tabId: newTabId }
    useForkConversation->>useForkConversation: Copy source config (cwd, customPath, SSH, model…)
    useForkConversation->>useForkConversation: setSessions([...prev, newSession])
    useForkConversation->>useForkConversation: setActiveSessionId(newSession.id)
    useForkConversation->>window.maestro.process: spawn({ sessionId, toolType, cwd, prompt, … })
    window.maestro.process-->>User: New forked agent session appears
Loading

Reviews (1): Last reviewed commit: "fix: copy cwd and fullPath from source s..." | Re-trigger Greptile

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🧹 Nitpick comments (1)
src/renderer/hooks/agent/useForkConversation.ts (1)

12-17: Keep this callback stable.

Capturing sessions here means every streamed log update recreates handleForkConversation. That new function is threaded into memoized transcript items, so streaming a response now invalidates memoization and re-renders the whole visible log list. Reading current state from a ref/store getter would avoid that churn.

Also applies to: 235-235

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/renderer/hooks/agent/useForkConversation.ts` around lines 12 - 17, The
callback handleForkConversation inside useForkConversation is unstable because
it closes over the sessions array, causing it to be recreated on every streamed
log update; change it to not capture sessions directly by using a stable
ref/getter for current sessions (e.g., sessionsRef.current or a store getter)
and wrap handleForkConversation in useCallback with minimal deps, then update
state via the functional updater form of setSessions and setActiveSessionId as
needed; ensure you only reference stable symbols (useForkConversation,
handleForkConversation, sessionsRef/current getter, setSessions functional
updater, setActiveSessionId) so memoized transcript items no longer re-render on
stream updates.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/renderer/components/TerminalOutput.tsx`:
- Around line 920-924: The fork button currently passes the rendered list index
(index from filteredLogs) to onForkConversation, which causes slicing of
sourceTab.logs at the wrong position; instead pass a raw log boundary that
identifies the original message in sourceTab.logs (e.g., pass the log's unique
id or compute its index in sourceTab.logs and pass that), or simply pass the log
object itself and let useForkConversation locate the correct index by matching
id/timestamp; update the button's onClick from onForkConversation(index) to
onForkConversation(logId || originalIndex || log) and adjust useForkConversation
to resolve the true sourceTab.logs index before slicing.

In `@src/renderer/hooks/agent/useForkConversation.ts`:
- Around line 31-42: The fork serializer currently drops image-backed turns by
only using log.text and hardcoding hasImages: false; update
useForkConversation's formatting logic (see formattedContext and slicedLogs) to
preserve attachment data by including each log's attachments/hasImages metadata
when present (e.g., carry attachments array or a flag alongside the role/text),
and compute a derived hasImages = slicedLogs.some(l => l.attachments?.length >
0) to pass into the spawn/fork call instead of false; also mirror the same
preservation for the other serializer path referenced around spawn (the block
that currently sets hasImages: false) so the forked conversation receives the
original attachments and image flag.
- Around line 80-96: createMergedSession() seeds the fork with projectRoot-based
shellCwd and an initial terminal tab at projectRoot, but the code only copies
session.cwd and session.fullPath into newSession; update the fork to preserve
the source working dir by also copying the shell/terminal working directory:
assign newSession.shellCwd = session.shellCwd (or session.cwd if shellCwd is
undefined) and update the initial terminal tab's working dir (the tab object
returned in newSession.aiTabs[0] or the initial terminal tab structure) to the
source session's cwd/fullPath so the forked terminal opens in the same
subdirectory.
- Around line 73-84: The new fork is being seeded with a single synthetic entry
(userContextLog) so the forked tab loses the original turns; change the
createMergedSession call so mergedLogs contains the actual sliced conversation
entries (the array holding the original turns up to the fork point) instead of
just userContextLog — e.g., use mergedLogs: [forkNotice, ...slicedLogs] (or the
existing variable name that holds the sliced logs), optionally appending a
context message if needed, and remove/replace userContextLog accordingly so
replay/delete/inspection operate on the real copied history.

---

Nitpick comments:
In `@src/renderer/hooks/agent/useForkConversation.ts`:
- Around line 12-17: The callback handleForkConversation inside
useForkConversation is unstable because it closes over the sessions array,
causing it to be recreated on every streamed log update; change it to not
capture sessions directly by using a stable ref/getter for current sessions
(e.g., sessionsRef.current or a store getter) and wrap handleForkConversation in
useCallback with minimal deps, then update state via the functional updater form
of setSessions and setActiveSessionId as needed; ensure you only reference
stable symbols (useForkConversation, handleForkConversation, sessionsRef/current
getter, setSessions functional updater, setActiveSessionId) so memoized
transcript items no longer re-render on stream updates.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 21d99e96-395f-4419-8af5-b8096804238a

📥 Commits

Reviewing files that changed from the base of the PR and between 796343e and 29b0d9e.

📒 Files selected for processing (8)
  • src/renderer/App.tsx
  • src/renderer/components/MainPanel/MainPanel.tsx
  • src/renderer/components/MainPanel/MainPanelContent.tsx
  • src/renderer/components/MainPanel/types.ts
  • src/renderer/components/TerminalOutput.tsx
  • src/renderer/hooks/agent/index.ts
  • src/renderer/hooks/agent/useForkConversation.ts
  • src/renderer/hooks/props/useMainPanelProps.ts

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

♻️ Duplicate comments (1)
src/renderer/hooks/agent/useForkConversation.ts (1)

79-90: ⚠️ Potential issue | 🟠 Major

Seed the forked tab with the copied transcript, not the synthesized fork prompt.

mergedLogs: [forkNotice, userContextLog] means the new session does not actually contain the conversation up to the selected message; it only contains a system notice plus the generated context prompt. That breaks the core fork UX from #205 because replay/delete/inspection in the fork operate on a summary prompt instead of the copied history. Keep contextMessage for spawn.prompt, but seed createMergedSession() with the sliced logs themselves.

Suggested direction
-			const userContextLog: LogEntry = {
-				id: `fork-context-${Date.now()}`,
-				timestamp: Date.now(),
-				source: 'user',
-				text: contextMessage,
-			};
-
 			const { session: newSession, tabId: newTabId } = createMergedSession({
 				name: forkName,
 				projectRoot: session.projectRoot,
 				toolType: session.toolType,
-				mergedLogs: [forkNotice, userContextLog],
+				mergedLogs: [forkNotice, ...slicedLogs],
 				saveToHistory: true,
 			});
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/renderer/hooks/agent/useForkConversation.ts` around lines 79 - 90, The
fork currently seeds createMergedSession(...) with only forkNotice and
userContextLog (mergedLogs: [forkNotice, userContextLog]) which stores a
synthesized prompt instead of the actual conversation; change
createMergedSession(...) to pass the sliced/copied conversation logs (the array
you produce when slicing the session up to the selected message) as mergedLogs
so the new session contains the real transcript; keep contextMessage
(userContextLog) for spawn.prompt or whatever field uses the synthesized prompt
but do not replace the mergedLogs with it — update the call to
createMergedSession(...) to use the slicedLogs/copyOfLogs variable instead of
[forkNotice, userContextLog] and ensure spawn.prompt (or equivalent) still
receives contextMessage.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@src/renderer/hooks/agent/useForkConversation.ts`:
- Around line 79-90: The fork currently seeds createMergedSession(...) with only
forkNotice and userContextLog (mergedLogs: [forkNotice, userContextLog]) which
stores a synthesized prompt instead of the actual conversation; change
createMergedSession(...) to pass the sliced/copied conversation logs (the array
you produce when slicing the session up to the selected message) as mergedLogs
so the new session contains the real transcript; keep contextMessage
(userContextLog) for spawn.prompt or whatever field uses the synthesized prompt
but do not replace the mergedLogs with it — update the call to
createMergedSession(...) to use the slicedLogs/copyOfLogs variable instead of
[forkNotice, userContextLog] and ensure spawn.prompt (or equivalent) still
receives contextMessage.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 4eef1d53-f6ec-4c37-9fdd-edccf61a035c

📥 Commits

Reviewing files that changed from the base of the PR and between 29b0d9e and 5488706.

📒 Files selected for processing (5)
  • src/renderer/components/MainPanel/MainPanelContent.tsx
  • src/renderer/components/MainPanel/types.ts
  • src/renderer/components/TerminalOutput.tsx
  • src/renderer/hooks/agent/useForkConversation.ts
  • src/renderer/hooks/props/useMainPanelProps.ts
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/renderer/hooks/props/useMainPanelProps.ts
  • src/renderer/components/TerminalOutput.tsx

chr1syy and others added 3 commits April 11, 2026 09:50
…tory

Add "Fork conversation from here" action to AI response and user messages
in the chat view. Clicking the GitFork icon creates a new session with
conversation history truncated at the selected message, formats context,
and auto-spawns the agent with the forked context.

Follows the existing "Send Context to Agent" pattern from
useMergeTransferHandlers.ts. Creates a new session via createMergedSession()
with source session config (custom model, SSH, env vars) carried over.

Files changed:
- New: src/renderer/hooks/agent/useForkConversation.ts (hook)
- Modified: TerminalOutput.tsx (fork button on ai/user messages)
- Modified: MainPanel types/content/component (prop threading)
- Modified: useMainPanelProps.ts (deps wiring)
- Modified: App.tsx (hook instantiation and dep passing)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Without this, forked sessions would default to projectRoot even if the
source agent had navigated to a subdirectory, causing a mismatch between
the new session's displayed path and the actual spawn directory.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…ool Output, copy shellCwd

- Pass log.id instead of visual index to useForkConversation so search
  filtering and consecutive-entry collapsing cannot shift the fork point
- Label stdout entries as "Tool Output" instead of "Assistant" in fork context
- Copy shellCwd and terminal tab cwd from source session when forking

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@chr1syy chr1syy force-pushed the feat/fork-conversation branch from 5488706 to e335372 Compare April 11, 2026 07:52
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

♻️ Duplicate comments (1)
src/renderer/hooks/agent/useForkConversation.ts (1)

86-90: ⚠️ Potential issue | 🟠 Major

Seed the forked tab with the real sliced history, not one synthetic context entry.

mergedLogs: [forkNotice, userContextLog] still collapses the copied branch into a single synthetic user message, so the new tab cannot inspect, replay, or delete the original turns up to the fork point. The synthesized prompt can stay, but the tab history should be built from slicedLogs.

Suggested fix
 			const { session: newSession, tabId: newTabId } = createMergedSession({
 				name: forkName,
 				projectRoot: session.projectRoot,
 				toolType: session.toolType,
-				mergedLogs: [forkNotice, userContextLog],
+				mergedLogs: [forkNotice, ...slicedLogs],
 				saveToHistory: true,
 			});
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/renderer/hooks/agent/useForkConversation.ts` around lines 86 - 90, The
fork currently seeds the new tab with mergedLogs: [forkNotice, userContextLog],
which collapses history; update the createMergedSession call to include the real
sliced history by replacing or appending mergedLogs with slicedLogs (preserving
forkNotice/userContextLog as synthetic context if desired) so the new
session/tab (variables newSession, newTabId) receives the actual slicedLogs
rather than a single synthetic entry; ensure the mergedLogs argument uses
slicedLogs (or [...slicedLogs, forkNotice, userContextLog] if you want synthetic
prompt plus full turns) when calling createMergedSession.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/renderer/hooks/agent/useForkConversation.ts`:
- Line 53: The fork name uses the wrong prefix; change the string assigned to
forkName in useForkConversation (the line creating const forkName = `Fork:
${sessionName}`) to use the documented prefix "Forked: " (i.e., const forkName =
`Forked: ${sessionName}`) so the new session/tab label matches the spec and any
tests relying on that exact text.
- Around line 109-114: The fork logic is copying agent overrides but omits the
customEffort field, so forks lose the source session's effort override; update
the fork code where newSession is populated from session (the block assigning
customPath, customArgs, customEnvVars, customModel, customContextWindow,
sessionSshRemoteConfig) to also copy customEffort (i.e., set
newSession.customEffort = session.customEffort), and make the same change in the
second analogous location that populates newSession from session.
- Around line 37-46: The serialized context currently filters and maps
log.source only for 'user', 'ai', and 'stdout', which drops 'tool' logs; update
the filtering in useForkConversation (where formattedContext is built) to
include logs with source === 'tool' and adjust the mapping logic so that
log.source === 'tool' maps to the role label "Tool Output" (keep 'stdout' also
labeled "Tool Output"), ensuring tool results are preserved in the forked
prompt.

---

Duplicate comments:
In `@src/renderer/hooks/agent/useForkConversation.ts`:
- Around line 86-90: The fork currently seeds the new tab with mergedLogs:
[forkNotice, userContextLog], which collapses history; update the
createMergedSession call to include the real sliced history by replacing or
appending mergedLogs with slicedLogs (preserving forkNotice/userContextLog as
synthetic context if desired) so the new session/tab (variables newSession,
newTabId) receives the actual slicedLogs rather than a single synthetic entry;
ensure the mergedLogs argument uses slicedLogs (or [...slicedLogs, forkNotice,
userContextLog] if you want synthetic prompt plus full turns) when calling
createMergedSession.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 1f444c7a-1a90-41aa-9dea-257702e6466c

📥 Commits

Reviewing files that changed from the base of the PR and between 5488706 and e335372.

📒 Files selected for processing (8)
  • src/renderer/App.tsx
  • src/renderer/components/MainPanel/MainPanel.tsx
  • src/renderer/components/MainPanel/MainPanelContent.tsx
  • src/renderer/components/MainPanel/types.ts
  • src/renderer/components/TerminalOutput.tsx
  • src/renderer/hooks/agent/index.ts
  • src/renderer/hooks/agent/useForkConversation.ts
  • src/renderer/hooks/props/useMainPanelProps.ts
✅ Files skipped from review due to trivial changes (2)
  • src/renderer/hooks/agent/index.ts
  • src/renderer/App.tsx
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/renderer/hooks/props/useMainPanelProps.ts
  • src/renderer/components/TerminalOutput.tsx

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (3)
src/renderer/hooks/agent/useForkConversation.ts (3)

53-53: ⚠️ Potential issue | 🟡 Minor

Use the documented Forked: prefix.

Line 53 still creates Fork: ${sessionName} even though the linked issue/spec calls for Forked: ${sessionName}. Small mismatch, but it will drift from the expected session/tab label.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/renderer/hooks/agent/useForkConversation.ts` at line 53, The session
label uses the wrong prefix: change the forkName construction in
useForkConversation (the const forkName = `Fork: ${sessionName}`) to use the
documented prefix by setting it to `Forked: ${sessionName}`; search for any
other uses of the `Fork: ` literal in useForkConversation and replace them with
`Forked: ` to keep labels consistent with the spec.

35-46: ⚠️ Potential issue | 🟠 Major

Include tool logs in the serialized fork context.

LogEntry.source supports both stdout and tool, but this filter only preserves stdout. Forking after file/search/edit tool turns will therefore drop part of the state the assistant responded to, and the new branch starts from incomplete context. tool should be included and labeled the same as stdout (“Tool Output”).

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/renderer/hooks/agent/useForkConversation.ts` around lines 35 - 46, The
filtered context currently excludes logs with source 'tool', causing tool
outputs to be dropped when building formattedContext; update the filter on
slicedLogs in useForkConversation (formattedContext) to include log.source ===
'tool' alongside 'stdout', and adjust the role mapping so that both log.source
=== 'stdout' and log.source === 'tool' map to 'Tool Output' (keeping 'user' ->
'User' and 'ai' -> 'Assistant').

79-90: ⚠️ Potential issue | 🟠 Major

Seed the forked tab with the copied turns, not just a synthetic summary.

createMergedSession() uses mergedLogs as the initial AI-tab history, so [forkNotice, userContextLog] means the forked session UI no longer contains the original turns up to the selected message. That breaks replay/delete/inspection on the fork and does not meet the “copy conversation history up to and including the selected message” objective. Use the sliced logs themselves here (optionally prefixed with forkNotice) and keep the synthesized prompt only for the initial spawn if needed.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/renderer/hooks/agent/useForkConversation.ts` around lines 79 - 90, The
mergedLogs passed into createMergedSession is currently only [forkNotice,
userContextLog], which drops the original turns; change it to include the sliced
conversation turns up to the selected message (e.g., use slicedLogs or the
variable holding the copied turns) — optionally prefix with forkNotice — so
createMergedSession({ ..., mergedLogs: [forkNotice, ...slicedLogs] }) (keep
userContextLog only as the spawn prompt if needed) to ensure the forked session
UI contains the original history for replay/delete/inspection.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/renderer/hooks/agent/useForkConversation.ts`:
- Around line 94-98: The new tab is set awaitingSessionId = true when spawning a
provider session (newTab in useForkConversation), but the error/catch paths only
reset state and thinkingStartTime so awaitingSessionId remains true and the fork
UI can get stuck; update the spawn failure handlers (the promise rejection/catch
blocks handling the spawn in useForkConversation, and the other similar catch
around the later spawn flow) to also set newTab.awaitingSessionId = false (and
clear any related flags) so the tab is not left permanently awaiting a session
id.

---

Duplicate comments:
In `@src/renderer/hooks/agent/useForkConversation.ts`:
- Line 53: The session label uses the wrong prefix: change the forkName
construction in useForkConversation (the const forkName = `Fork:
${sessionName}`) to use the documented prefix by setting it to `Forked:
${sessionName}`; search for any other uses of the `Fork: ` literal in
useForkConversation and replace them with `Forked: ` to keep labels consistent
with the spec.
- Around line 35-46: The filtered context currently excludes logs with source
'tool', causing tool outputs to be dropped when building formattedContext;
update the filter on slicedLogs in useForkConversation (formattedContext) to
include log.source === 'tool' alongside 'stdout', and adjust the role mapping so
that both log.source === 'stdout' and log.source === 'tool' map to 'Tool Output'
(keeping 'user' -> 'User' and 'ai' -> 'Assistant').
- Around line 79-90: The mergedLogs passed into createMergedSession is currently
only [forkNotice, userContextLog], which drops the original turns; change it to
include the sliced conversation turns up to the selected message (e.g., use
slicedLogs or the variable holding the copied turns) — optionally prefix with
forkNotice — so createMergedSession({ ..., mergedLogs: [forkNotice,
...slicedLogs] }) (keep userContextLog only as the spawn prompt if needed) to
ensure the forked session UI contains the original history for
replay/delete/inspection.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 0efff5d2-07ac-48b8-82b2-fa87a53af925

📥 Commits

Reviewing files that changed from the base of the PR and between e335372 and bcb7369.

📒 Files selected for processing (1)
  • src/renderer/hooks/agent/useForkConversation.ts

- Include 'tool' log source in serialized fork context
- Change fork name prefix from "Fork:" to "Forked:"
- Copy customEffort from source session and pass to spawn
- Clear awaitingSessionId on spawn failure to prevent stuck UI

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@chr1syy
Copy link
Copy Markdown
Contributor Author

chr1syy commented Apr 11, 2026

@pedramamini good to go for your review

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant